package com.demo.spring.redisson;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.redisson.api.NatMapper;
import org.redisson.config.ReadMode;
import org.redisson.config.SslProvider;
import org.redisson.config.SubscriptionMode;
import org.redisson.config.TransportMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.net.URL;
import java.util.*;

@Configuration
@ConfigurationProperties(prefix = "spring.redisson")
public class RedissonProps {

    public boolean available(){
        return singleServerConfig!=null
                || masterSlaveServersConfig!=null
                || sentinelServersConfig!=null
                || clusterServersConfig!=null
                || replicatedServersConfig!=null;
    }

    private SingleServerConfig singleServerConfig;
    public void setSingleServerConfig(SingleServerConfig singleServerConfig){this.singleServerConfig = singleServerConfig;}
    public SingleServerConfig getSingleServerConfig(){return this.singleServerConfig;}

    private MasterSlaveServersConfig masterSlaveServersConfig;
    public void setMasterSlaveServersConfig(MasterSlaveServersConfig masterSlaveServersConfig){this.masterSlaveServersConfig = masterSlaveServersConfig;}
    public MasterSlaveServersConfig getMasterSlaveServersConfig(){return this.masterSlaveServersConfig;}

    private SentinelServersConfig sentinelServersConfig;
    public void setSentinelServersConfig(SentinelServersConfig sentinelServersConfig){this.sentinelServersConfig = sentinelServersConfig;}
    public SentinelServersConfig getSentinelServersConfig(){return this.sentinelServersConfig;}

    private ClusterServersConfig clusterServersConfig;
    public void setClusterServersConfig(ClusterServersConfig clusterServersConfig){this.clusterServersConfig = clusterServersConfig;}
    public ClusterServersConfig getClusterServersConfig(){return this.clusterServersConfig;}

    private ReplicatedServersConfig replicatedServersConfig;
    public void setReplicatedServersConfig(ReplicatedServersConfig replicatedServersConfig){this.replicatedServersConfig = replicatedServersConfig;}
    public ReplicatedServersConfig getReplicatedServersConfig(){return this.replicatedServersConfig;}

    @Getter@Setter
    private int threads = 16;
    @Getter@Setter
    private int nettyThreads = 32;
    @Getter@Setter
    private boolean referenceEnabled = true;
    @Getter@Setter
    private TransportMode transportMode = TransportMode.NIO;
    @Getter@Setter
    private long lockWatchdogTimeout = 30 * 1000;
    @Getter@Setter
    private boolean keepPubSubOrder = true;
    @Getter@Setter
    private boolean decodeInExecutor = false;
    @Getter@Setter
    private boolean useScriptCache = false;
    @Getter@Setter
    private int minCleanUpDelay = 5;
    @Getter@Setter
    private int maxCleanUpDelay = 30*60;
    @Getter@Setter
    private int cleanUpKeysAmount = 100;
    @Getter@Setter
    private boolean useThreadClassLoader = true;


    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class SingleServerConfig extends BaseConfig{
        private String address;
        private int subscriptionConnectionMinimumIdleSize = 1;
        private int subscriptionConnectionPoolSize = 50;
        private int connectionMinimumIdleSize = 24;
        private int connectionPoolSize = 64;
        private int database = 0;
        private long dnsMonitoringInterval = 5000;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class MasterSlaveServersConfig extends BaseMasterSlaveServersConfig{
        private Set<String> slaveAddresses = new HashSet<String>();
        private String masterAddress;
        private int database = 0;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class SentinelServersConfig extends BaseMasterSlaveServersConfig{
        private List<String> sentinelAddresses = Collections.emptyList();
        private NatMapper natMapper = NatMapper.direct();
        private String masterName;
        private int database = 0;
        private int scanInterval = 1000;
        private boolean checkSentinelsList = true;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class ClusterServersConfig extends BaseMasterSlaveServersConfig{
        private NatMapper natMapper = NatMapper.direct();
        private List<String> nodeAddresses = Collections.emptyList();
        private int scanInterval = 5000;
        private boolean checkSlotsCoverage = true;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class ReplicatedServersConfig extends BaseMasterSlaveServersConfig{
        private List<String> nodeAddresses = Collections.emptyList();
        private int scanInterval = 1000;
        private int database = 0;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class BaseMasterSlaveServersConfig extends BaseConfig {
        private int slaveConnectionMinimumIdleSize = 24;
        private int slaveConnectionPoolSize = 64;
        private int failedSlaveReconnectionInterval = 3000;
        private int failedSlaveCheckInterval = 180000;
        private int masterConnectionMinimumIdleSize = 24;
        private int masterConnectionPoolSize = 64;
        private ReadMode readMode = ReadMode.SLAVE;
        private SubscriptionMode subscriptionMode = SubscriptionMode.MASTER;
        private int subscriptionConnectionMinimumIdleSize = 1;
        private int subscriptionConnectionPoolSize = 50;
        private long dnsMonitoringInterval = 5000;
    }

    @Data
    public static class BaseConfig {
        private int idleConnectionTimeout = 10000;
        private int connectTimeout = 10000;
        private int timeout = 3000;
        private int retryAttempts = 3;
        private int retryInterval = 1500;
        private String password;
        private String username;
        private int subscriptionsPerConnection = 5;
        private String clientName;
        private boolean sslEnableEndpointIdentification = true;
        private SslProvider sslProvider = SslProvider.JDK;
        private URL sslTruststore;
        private String sslTruststorePassword;
        private URL sslKeystore;
        private String sslKeystorePassword;
        private int pingConnectionInterval = 30000;
        private boolean keepAlive;
        private boolean tcpNoDelay;
    }
}
